<template>
  <el-tree-select
    ref="selectTree"
    v-model="modelValue"
    :size="size"
    :data="options"
    :props="objMap"
    :multiple="multiple"
    :clearable="clearable"
    :collapse-tags="collapseTags"
    @remove-tag="removeSelectedNodes"
    @clear="removeSelectedNode"
    @change="changeSelectedNodes"
    :node-key="nodeKey"
    :value-key="nodeKey"
    :render-after-expand="false"
    :show-checkbox="multiple"
    :check-strictly="checkStrictly"
    check-on-click-node
    :expand-on-click-node="true"
    :check-on-click-node="multiple"
    :highlight-current="!multiple"
    :default-expanded-keys="defaultExpandedKeys"
    :filter-node-method="filterNode"
    @node-click="handleNodeClick"
    @check-change="handleCheckChange"
    style="width: 100%"
  />
</template>
<script setup lang="ts">
import { ElTreeSelect } from 'element-plus'

const props = defineProps({
  /* 配置项 */
  objMap: {
    type: Object,
    default: () => {
      return {
        value: 'id', // ID字段名
        label: 'label', // 显示名称
        children: 'children' // 子级字段名
      }
    }
  },
  /* 自动收起 */
  accordion: {
    type: Boolean,
    default: () => {
      return false
    }
  },
  /**当前双向数据绑定的值 */
  selectValue: {
    type: Array,
    default: () => []
  },
  /**当前的数据 */
  options: {
    type: Array,
    default: () => []
  },
  /**输入框内部的文字 */
  placeholder: {
    type: String,
    default: ''
  },
  // 配置是否可多选
  multiple: {
    type: Boolean,
    default() {
      return false;
    }
  },
  // 配置是否可清空选择
  clearable: {
    type: Boolean,
    default() {
      return false;
    }
  },
  // 配置多选时是否将选中值按文字的形式展示
  collapseTags: {
    type: Boolean,
    default() {
      return false;
    }
  },
  nodeKey: {
    type: String,
    default() {
      return 'id';
    }
  },
  // 显示复选框情况下，是否严格遵循父子不互相关联
  checkStrictly: {
    type: Boolean,
    default() {
      return false;
    }
  },
  size: {
    type: String,
    default() {
      return 'small';
    }
  },
})

const modelValue = ref([])
const selectTree = ref(ElTreeSelect);

const emit = defineEmits(['update:value', 'change']);

const defaultExpandedKeys = computed(() => {
  return props.multiple ? props.selectValue : [props.selectValue] || []
});


function initHandle() {
  // 多选
  if(props.multiple) {
    const selectedData: any = props.selectValue
    nextTick(() => {
      selectTree.value.setCheckedKeys(selectedData)
    })
  } else {
    const selectedValue: any = props.selectValue[0] || null
    nextTick(() => {
      selectTree.value.setCheckedKey(selectedValue)
    })
  }
}
// 单选，节点被点击时的回调,返回被点击的节点数据
function handleNodeClick(node: any) {
  emit('change', node[props.objMap.value])
  selectTree.value.blur()
  selectFilterData('')
}
// 多选，节点勾选状态发生变化时的回调
function handleCheckChange() {
  const nodes = selectTree.value.getCheckedNodes()
  const value = nodes.map((item: any) => item[props.nodeKey])
  emit('change', value)
}
// 多选,删除任一select选项的回调
function removeSelectedNodes(val: any) {
  selectTree.value.setChecked(val, false);
  const node = selectTree.value.getNode(val)
  if (!props.checkStrictly && node.childNodes.length > 0) {
    treeToList(node).forEach((each: any) => {
      if (each.childNodes.length <= 0) {
        selectTree.value.setChecked(each, false);
      }
    })
    handleCheckChange()
  }
}
function treeToList(tree: []) {
  let queen = [].concat(tree)
  let out = []
  while (queen.length) {
    let first: any = queen.shift();
    if (first.childNodes) {
      queen = queen.concat(first.childNodes);
    }
    out.push(first);
  }
  return out;
}
// 单选,清空select输入框的回调
function removeSelectedNode() {
  selectTree.value.setCurrentKey(null)
}
// 多选，清空所有勾选
function clearSelectedNodes() {
  const checkedKeys = selectTree.value.getCheckedKeys()
  for (let i = 0; i < checkedKeys.length; i++) {
    selectTree.value.setChecked(checkedKeys[i], false);
  }
}
// 选中的select选项改变的回调
function changeSelectedNodes(selectedData: []) {
  if(props.multiple && selectedData.length <= 0) {
    clearSelectedNodes()
  }
}
function selectFilterData(val: any) {
  selectTree.value.filter(val)
}
function filterNode(value: any, data: any) {
  if (!value) return true
  return data[props.objMap['label']].indexOf(value) !== -1
}

onMounted(() => {
  initHandle()
})

watch(
  () => props.selectValue,
  () => {
    initHandle();
  }
)
</script>

<style lang="scss" scoped>
@import "@/assets/styles/variables.module.scss";
.el-scrollbar .el-scrollbar__view .el-select-dropdown__item {
  padding: 0;
  background-color: #fff;
  height: auto;
}

.el-select-dropdown__item.selected {
  font-weight: normal;
}

ul li .el-tree .el-tree-node__content {
  height: auto;
  padding: 0 20px;
  box-sizing: border-box;
}

:deep(.el-tree-node__content:hover),
:deep(.el-tree-node__content:active),
:deep(.is-current > div:first-child),
:deep(.el-tree-node__content:focus) {
  background-color: mix(#fff, $--color-primary, 90%);
  color: $--color-primary;
}
</style>
